package com.twinkle.user.biz.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.base.Preconditions;
import com.twinkle.framework.biz.context.holder.LoginUserContextHolder;
import com.twinkle.framework.common.exception.BusinessException;
import com.twinkle.framework.common.response.Response;
import com.twinkle.framework.common.utils.JsonUtils;
import com.twinkle.framework.common.utils.ParamUtils;
import com.twinkle.user.biz.constant.RedisKeyConstants;
import com.twinkle.user.biz.constant.RoleConstants;
import com.twinkle.user.biz.enums.DeletedEnum;
import com.twinkle.user.biz.enums.ResponseCodeEnum;
import com.twinkle.user.biz.enums.SexEnum;
import com.twinkle.user.biz.enums.StatusEnum;
import com.twinkle.user.biz.mapper.RoleMapper;
import com.twinkle.user.biz.mapper.UserMapper;
import com.twinkle.user.biz.mapper.UserRoleRelMapper;
import com.twinkle.user.biz.model.entity.Role;
import com.twinkle.user.biz.model.entity.User;
import com.twinkle.user.biz.model.entity.UserRoleRel;
import com.twinkle.user.biz.model.vo.UpdateUserInfoReqVO;
import com.twinkle.user.biz.rpc.DistributedIdGeneratorRpcService;
import com.twinkle.user.biz.rpc.OssRpcService;
import com.twinkle.user.biz.service.UserService;
import com.twinkle.user.dto.req.FindUserByIdReqDTO;
import com.twinkle.user.dto.req.FindUserByPhoneReqDTO;
import com.twinkle.user.dto.req.RegisterUserReqDTO;
import com.twinkle.user.dto.req.UpdateUserPasswordReqDTO;
import com.twinkle.user.dto.resp.FindUserByIdRspDTO;
import com.twinkle.user.dto.resp.FindUserByPhoneRspDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private OssRpcService ossRpcService;
    @Resource
    private UserRoleRelMapper userRoleRelMapper;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private DistributedIdGeneratorRpcService distributedIdGeneratorRpcService;
    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 用户信息本地缓存
     */
    private static final Cache<Long, FindUserByIdRspDTO> LOCAL_CACHE = Caffeine.newBuilder()
            .initialCapacity(10000) // 设置初始容量为 10000 个条目
            .maximumSize(10000) // 设置缓存的最大容量为 10000 个条目
            .expireAfterWrite(1, TimeUnit.HOURS) // 设置缓存条目在写入后 1 小时过期
            .build();

    @Override
    public Response<?> updateUserInfo(UpdateUserInfoReqVO updateUserInfoReqVO) {
        User user = new User();
        user.setId(LoginUserContextHolder.getUserId());

        boolean needUpdate = false;

        MultipartFile avatarFile = updateUserInfoReqVO.getAvatar();
        if (Objects.nonNull(avatarFile)) {
            String avatar = ossRpcService.uploadFile(avatarFile);
            if (StringUtils.isBlank(avatar)) {
                throw new BusinessException(ResponseCodeEnum.UPLOAD_AVATAR_FAIL);
            }
            user.setAvatar(avatar);
            needUpdate = true;
        }
        // 昵称
        String nickname = updateUserInfoReqVO.getNickname();
        if (StringUtils.isNotBlank(nickname)) {
            Preconditions.checkArgument(ParamUtils.checkNickname(nickname), ResponseCodeEnum.NICK_NAME_VALID_FAIL.getErrorMessage());
            user.setNickname(nickname);
            needUpdate = true;
        }

        // 小哈书号
        String xiaohashuId = updateUserInfoReqVO.getXiaohashuId();
        if (StringUtils.isNotBlank(xiaohashuId)) {
            Preconditions.checkArgument(ParamUtils.checkXiaohashuId(xiaohashuId), ResponseCodeEnum.XIAOHASHU_ID_VALID_FAIL.getErrorMessage());
            user.setXiaohashuId(xiaohashuId);
            needUpdate = true;
        }

        // 性别
        Integer sex = updateUserInfoReqVO.getSex();
        if (Objects.nonNull(sex)) {
            Preconditions.checkArgument(SexEnum.isValid(sex), ResponseCodeEnum.SEX_VALID_FAIL.getErrorMessage());
            user.setSex(sex);
            needUpdate = true;
        }

        // 生日
        LocalDate birthday = updateUserInfoReqVO.getBirthday();
        if (Objects.nonNull(birthday)) {
            user.setBirthday(birthday);
            needUpdate = true;
        }

        // 个人简介
        String introduction = updateUserInfoReqVO.getIntroduction();
        if (StringUtils.isNotBlank(introduction)) {
            Preconditions.checkArgument(ParamUtils.checkLength(introduction, 100), ResponseCodeEnum.INTRODUCTION_VALID_FAIL.getErrorMessage());
            user.setIntroduction(introduction);
            needUpdate = true;
        }

        // 背景图
        MultipartFile backgroundImgFile = updateUserInfoReqVO.getBackgroundImg();
        if (Objects.nonNull(backgroundImgFile)) {
            String backgroundImg = ossRpcService.uploadFile(backgroundImgFile);
            if (StringUtils.isBlank(backgroundImg)) {
                throw new BusinessException(ResponseCodeEnum.UPLOAD_BACKGROUND_IMG_FAIL);
            }
            user.setBackgroundImg(backgroundImg);
            needUpdate = true;
        }

        if (needUpdate) {
            // 更新用户信息
            user.setUpdateTime(LocalDateTime.now());
            userMapper.updateByPrimaryKeySelective(user);
        }
        return Response.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response<Long> register(RegisterUserReqDTO registerUserReqDTO) {
        String phone = registerUserReqDTO.getPhone();

        // 先判断该手机号是否已被注册
        User userDO1 = userMapper.selectByEmail(phone);

        // 若已注册，则直接返回用户 ID
        if (Objects.nonNull(userDO1)) {
            return Response.success(userDO1.getId());
        }

        // 否则注册新用户
        // 获取全局自增的小哈书 ID
//        Long xiaohashuId = redisTemplate.opsForValue().increment(RedisKeyConstants.XIAOHASHU_ID_GENERATOR_KEY);
        String xiaohashuId = distributedIdGeneratorRpcService.getXiaohashuId();
        String userIdStr = distributedIdGeneratorRpcService.getUserId();
        Long userId = Long.valueOf(userIdStr);
        User userDO = User.builder()
                .id(userId)
                .phone(phone)
                .xiaohashuId(String.valueOf(xiaohashuId)) // 自动生成小红书号 ID
                .nickname("小红薯" + xiaohashuId) // 自动生成昵称, 如：小红薯10000
                .status(StatusEnum.ENABLED.getValue()) // 状态为启用
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .isDeleted(Boolean.valueOf(DeletedEnum.NO.getValue())) // 逻辑删除
                .build();

        // 添加入库
        userMapper.insert(userDO);

        // 给该用户分配一个默认角色
        UserRoleRel userRole = UserRoleRel.builder()
                .userId(userId)
                .roleId(RoleConstants.COMMON_USER_ROLE_ID)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .isDeleted(Boolean.valueOf(DeletedEnum.NO.getValue()))
                .build();
        userRoleRelMapper.insert(userRole);

        Role roleDO = roleMapper.selectByPrimaryKey(RoleConstants.COMMON_USER_ROLE_ID);

        // 将该用户的角色 ID 存入 Redis 中
        List<String> roles = new ArrayList<>(1);
        roles.add(roleDO.getRoleKey());

        String userRolesKey = RedisKeyConstants.buildUserRoleKey(userId);
        redisTemplate.opsForValue().set(userRolesKey, JsonUtils.toJsonString(roles));

        return Response.success(userId);
    }

    @Override
    public Response<FindUserByPhoneRspDTO> findByPhone(FindUserByPhoneReqDTO findUserByPhoneReqDTO) {
        String phone = findUserByPhoneReqDTO.getPhone();

        // 根据手机号查询用户信息
        User userDO = userMapper.selectByEmail(phone);

        // 判空
        if (Objects.isNull(userDO)) {
            throw new BusinessException(ResponseCodeEnum.USER_NOT_FOUND);
        }

        // 构建返参
        FindUserByPhoneRspDTO findUserByPhoneRspDTO = FindUserByPhoneRspDTO.builder()
                .id(userDO.getId())
                .password(userDO.getPassword())
                .build();

        return Response.success(findUserByPhoneRspDTO);
    }

    @Override
    public Response<?> updatePassword(UpdateUserPasswordReqDTO updateUserPasswordReqDTO) {
        // 获取当前请求对应的用户 ID
        Long userId = LoginUserContextHolder.getUserId();

        User user = User.builder()
                .id(userId)
                .password(updateUserPasswordReqDTO.getEncodePassword()) // 加密后的密码
                .updateTime(LocalDateTime.now())
                .build();
        // 更新密码
        userMapper.updateByPrimaryKeySelective(user);

        return Response.success();
    }

    @Override
    public Response<FindUserByIdRspDTO> findById(FindUserByIdReqDTO findUserByIdReqDTO) {
        Long userId = findUserByIdReqDTO.getId();
        //从本地缓存中查
        FindUserByIdRspDTO findUserByIdRspDTOLocalCache = LOCAL_CACHE.getIfPresent(userId);
        if (Objects.nonNull(findUserByIdRspDTOLocalCache)) {
            return Response.success(findUserByIdRspDTOLocalCache);
        }
        //从redis中查
        String userInfoKey = RedisKeyConstants.buildUserInfoKey(userId);
        String userInfoValue = redisTemplate.opsForValue().get(userInfoKey).toString();
        if(StringUtils.isNotBlank(userInfoValue)){
            FindUserByIdRspDTO findUserByIdRspDTO = JsonUtils.parseObject(userInfoValue, FindUserByIdRspDTO.class);
            threadPoolTaskExecutor.submit(() -> {
                if(Objects.nonNull(findUserByIdRspDTO)){
                    LOCAL_CACHE.put(userId, findUserByIdRspDTO);
                }
            });
            return Response.success(findUserByIdRspDTO);
        }
        User user = userMapper.selectByPrimaryKey(userId);
        if (Objects.isNull(user)) {
            threadPoolTaskExecutor.execute(() -> {
               long expireTime = 60 + RandomUtil.randomInt(60);
               redisTemplate.opsForValue().set(userInfoKey, "null",expireTime, TimeUnit.SECONDS);
            });
            throw new BusinessException(ResponseCodeEnum.USER_NOT_FOUND);
        }
        //从数据库查
        FindUserByIdRspDTO findUserByIdRspDTO = FindUserByIdRspDTO.builder()
                .nickName(user.getNickname())
                .id(userId)
                .avatar(user.getAvatar())
                .build();
        threadPoolTaskExecutor.submit(() ->{
            long expireSeconds = 60*60*24 + RandomUtil.randomInt(60*60*24);
            redisTemplate.opsForValue().set(userInfoKey, JsonUtils.toJsonString(user),expireSeconds, TimeUnit.SECONDS);
        });
        return Response.success(findUserByIdRspDTO);
    }
}
